﻿namespace EggOfPanKu.DeckBuilder
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Xml;

    using EggOfPanKu.DeckBuilder.Properties;


    using EggOfPanKu.Controls;
    using EggOfPanKu.Library.Objects;
    using EggOfPanKu.Library.Infrastructure;

    using My = Microsoft.Windows.Controls;
    
    public partial class DeckEditor : Window
    {
        #region Fields

        private Point _CanvasCenter;
        private Dictionary<string, Card> _CardDB;
        private CollectionViewSource _FilteredCardList;
        private string _ImagePath;
        private GameDeck _MyDeck;

        #endregion Fields

        #region Constructors

        public DeckEditor()
        {
            //Default Initialize Method
            InitializeComponent();
            //Initialize DeckBuilder Controls
            InitializeControls();

            string[] args = Environment.GetCommandLineArgs();
            if (args.Count() > 1)
            {
                OpenDeckFile(args[1]);
            }
        }

        #endregion Constructors

        #region Properties

        public Point CanvasCenter
        {
            get { return _CanvasCenter; }
            set { _CanvasCenter = value; }
        }

        public Dictionary<string, Card> CardDB
        {
            get { return _CardDB; }
            set { _CardDB = value; }
        }

        public CollectionViewSource FilteredCardList
        {
            get { return _FilteredCardList; }
            set { _FilteredCardList = value; }
        }

        public string ImagePath
        {
            get { return _ImagePath; }
            set { _ImagePath = value; }
        }

        public GameDeck MyDeck
        {
            get { return _MyDeck; }
            set { _MyDeck = value; }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Attempts to convert a given string into an integer.
        /// </summary>
        /// <param name="value">String value used in the conversion.</param>
        /// <returns>  Returns true if successful and false if not.</returns>
        public static bool IsInteger(string value)
        {
            try
            {
                Convert.ToInt32(value);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Initialize properties of this control
        /// </summary>
        public void InitializeControls()
        {
            //Initialize Properties
            ClearDeck();

            FilteredCardList = new CollectionViewSource();

            LblFilteredCount.Content = "No card database currently loaded.";

            CanvasCenter = new Point(DeckAreaCanvas.ActualWidth / 2, DeckAreaCanvas.ActualHeight / 2);

            //Initialize Paths
            ImagePath = ConfigManager.AppSettingsMgr.CardImagePath;

            //Try to Load Card.XML from UserXmlPath Setting
            string cardXmlFile = ConfigManager.AppSettingsMgr.CardXmlPath;
            if (System.IO.File.Exists(cardXmlFile))
            {
                LoadCardDB(cardXmlFile);
            }
            else
            {
                //Create Window and attach an event for Submitting a Card XML File
                LoadCards loadCardsWindow = new LoadCards();
                loadCardsWindow.SubmitCardFile += new LoadCards.CardFileSubmitHandler(LoadCardsWindow_SubmitCardFile);

                loadCardsWindow.Show();
            }
        }

        /// <summary>
        /// Static method used to find the Visual Parent of a given UI Element
        /// </summary>
        /// <typeparam name="T">Return type of the Parent</typeparam>
        /// <param name="element">UI Element that is being interrogated</param>
        /// <returns>Parent of the UI Element that matches the specified type</returns>
        private static T FindVisualParent<T>(UIElement element)
            where T : UIElement
        {
            UIElement parent = element;
            while (parent != null)
            {
                T correctlyTyped = parent as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                parent = VisualTreeHelper.GetParent(parent) as UIElement;
            }

            return null;
        }

        private CardControl FindControlByCard(Card card, bool getLast)
        {
            if (DeckAreaCanvas.Children.OfType<CardControl>().Count() > 0)
            {
                if (getLast)
                {
                    return DeckAreaCanvas.Children.OfType<CardControl>().LastOrDefault(c => c.Card == card);
                }
                else
                {
                    return DeckAreaCanvas.Children.OfType<CardControl>().FirstOrDefault(c => c.Card == card);
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Creates a new CardControl and adds it to the Canvas
        /// </summary>
        /// <param name="card">Card used in the creation of the CardControl</param>
        /// <param name="p">Point on the campus to add the control at</param>
        private void AddCardToCanvas(Card card, Point p)
        {
            //Initialize Card Control
            CardControl cardControl = new CardControl(card);

            cardControl.ControlID = DeckAreaCanvas.Children.Count + 1;
            cardControl.AddCard += new CardControl.AddCardHandler(AddCard_Click);
            cardControl.RemoveCard += new CardControl.RemoveCardHandler(RemoveCard_Click);
            cardControl.ViewCard += new CardControl.ViewCardHandler(ViewCard_Click);
            cardControl.SideViewCard += new CardControl.SideViewCardHandler(SideViewCard);

            //Add new Control to Canvas
            DeckAreaCanvas.Children.Add(cardControl);
            Canvas.SetLeft(cardControl, p.X);
            Canvas.SetTop(cardControl, p.Y);
            DeckAreaCanvas.BringToFront(cardControl);
        }

        private void AddCardToCanvas(Card card)
        {
            Point pEntry = new Point(0,0);
            //Are there card controls in the deck area?
            if (DeckAreaCanvas.Children.OfType<CardControl>().Count() > 0)
            {
                //Find a similarly named card
                CardControl sibling = FindControlByCard(card, true);

                if (sibling != null)
                {
                    //Found a copy
                    pEntry = sibling.TranslatePoint(new Point(0, 0), DeckAreaCanvas);
                }
                else
                {
                    //Didn't find a similar card, set to last card in the canvas
                    pEntry = DeckAreaCanvas.Children.OfType<CardControl>().Last()
                        .TranslatePoint(new Point(0, 0), DeckAreaCanvas);
                }
                //Slide the card down slightly
                pEntry.Y += 10;
            }
            else
            {
                //Default position, not sure how elegant we should get with this.
                pEntry = new Point(5, 5); 
            }

            AddCardToCanvas(card, pEntry);
        }

        /// <summary>
        /// Add a card to the in memory Deck
        /// </summary>
        /// <param name="card">Card to add to the Deck</param>
        private string AddCardToDeck(Card card)
        {
            string error = MyDeck.AddCard(card, 1);
            if (String.IsNullOrEmpty(error))
            {
                UpdateOverviewText();
                TxtDeck.Text = MyDeck.DeckAsText();
            }
            return error;
        }

        /// <summary>
        /// Event that captures the AddCard click.  
        /// </summary>
        /// <param name="sender">CardControl that initiated the AddCard event</param>
        /// <param name="e">Parameters that were passed along from the CardControl</param>
        private void AddCard_Click(object sender, RoutedEventArgs e)
        {
            CardControl cardControl = sender as CardControl;
            if (cardControl != null)
            {
                AddCard(cardControl.Card);
            }
        }

        private void AddCard(Card card)
        {
            string error = AddCardToDeck(card);
            if (String.IsNullOrEmpty(error))
                AddCardToCanvas(card);
            else
                MessageBox.Show(error, "Unable to add card"); 
        }

        /// <summary>
        /// Button event that will trigger the removal of all card from the canvas and in memory deck
        /// </summary>
        /// <param name="sender">Button that was clicked</param>
        /// <param name="e">Parameter that was passed with the event</param>
        private void BtnClearAll_Click(object sender, RoutedEventArgs e)
        {
            //Remove all Cards from the Deck and Canvas
            ClearCanvas();
            ClearDeck();
        }

        /// <summary>
        /// Button event to organize the layout of cards in the canvas
        /// </summary>
        /// <param name="sender">Button that was clicked</param>
        /// <param name="e">Parameter that was passed with the event</param>
        private void BtnOrganize_Click(object sender, RoutedEventArgs e)
        {
            //Remove all cards from the canvas
            ClearCanvas();

            //Re-add cards in an organized layout
            OrganizeCanvas();
        }

        /// <summary>
        /// Add Three of the same cards to the Canvas and Deck.  This event is triggered from the Card Grid
        /// </summary>
        /// <param name="sender">CardGrid row that represents the Cards to Add</param>
        /// <param name="e">Parameters passed with the event</param>
        private void CardDataGridAdd3_Click(object sender, RoutedEventArgs e)
        {
            //Create the Card, and add it 3 times
            Card newCard = GetCardFromContextMenu(sender as MenuItem);
            if (newCard != null)
            {
                for (int i = 0; i < 3; i++)
                {
                    AddCard(newCard);
                }
            }
        }

        /// <summary>
        /// Add a card to the Canvas and Deck.  This event is triggered from the Card Grid
        /// </summary>
        /// <param name="sender">CardGrid row that represents the Card to Add </param>
        /// <param name="e"></param>
        private void CardDataGridAdd_Click(object sender, RoutedEventArgs e)
        {
            Card newCard = GetCardFromContextMenu(sender as MenuItem);
            
            //Add the Card to the in memory Deck
            if (newCard != null)
                AddCard(newCard);
        }

        /// <summary>
        /// Open the selected card in the Preview window
        /// </summary>
        /// <param name="sender">CardGrid row that represents the Card to Preview</param>
        /// <param name="e"></param>
        private void CardDataGridPreview_Click(object sender, RoutedEventArgs e)
        {
            Card previewCard = GetCardFromContextMenu(sender as MenuItem);
            
            PreviewCard(previewCard);
        }

        /// <summary>
        /// Remove up to three instances of the selected Card from the Canvas and in memory Deck
        /// </summary>
        /// <param name="sender">CardGrid row that represents the Cards to remove</param>
        /// <param name="e"></param>
        private void CardDataGridRemove3_Click(object sender, RoutedEventArgs e)
        {
            Card deleteCard = GetCardFromContextMenu(sender as MenuItem);

            for (int i = 0; i < 3; i++)
            {
                RemoveCardFromCanvas(GetCardControlFromID(deleteCard.CardID));

                RemoveCardFromDeck(deleteCard);
            }
        }

        /// <summary>
        /// Remove the selected Card from teh Canvas and in memory Deck
        /// </summary>
        /// <param name="sender">CardGrid row that represents the Card to Remove</param>
        /// <param name="e"></param>
        private void CardDataGridRemove_Click(object sender, RoutedEventArgs e)
        {
            Card deleteCard = GetCardFromContextMenu(sender as MenuItem);
            
            RemoveCardFromCanvas(GetCardControlFromID(deleteCard.CardID));
            
            RemoveCardFromDeck(deleteCard);
        }

        private Card GetCardFromContextMenu(MenuItem menu)
        {
            if (menu == null)
            {
                return null;
            }
            else
            {
                return ((My.DataGrid)menu.DataContext).Items[(int)menu.CommandParameter] as Card;
            }
        }

        private CardControl GetCardControlFromID(string cardID)
        {
            if (String.IsNullOrEmpty(cardID))
            {
                return null;
            }
            else
            {
                return DeckAreaCanvas.Children.OfType<CardControl>().FirstOrDefault(ctl => ctl.Card.CardID == cardID);
            }
        }

        /// <summary>
        /// Previews the selected Card when the card row is double clicked
        /// </summary>
        /// <param name="sender">CardGrid row that was double clicked</param>
        /// <param name="e"></param>
        private void CardDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            Card selectedCard = ((My.DataGrid)sender).SelectedItem as Card;
            if (selectedCard != null)
                AddCard(selectedCard);
        }

        /// <summary>
        /// Handles the Drag event for "card" being dragged from the card Grid control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CardDataGrid_MouseMove(object sender, MouseEventArgs e)
        {
            //Only do the drag if the Left Mouse button is pressed (held down)
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                //Get the Visual Parent (DataGridRow) of the clicked cell
                DataGridRow row = FindVisualParent<DataGridRow>(e.OriginalSource as FrameworkElement);
                //If the row is not null, and is the selected row, then perform the drag
                if ((row != null) && row.IsSelected)
                {
                    try
                    {
                        // Perform the drag operation
                        Card selectedCard = (Card)row.Item;
                        DataObject dataObj = new DataObject("Card", selectedCard);
                        DragDropEffects finalDropEffect = DragDrop.DoDragDrop(row, dataObj, DragDropEffects.Move);
                    }
                    catch (System.Exception exc)
                    {
                        string t = exc.Message;
                    }
                }
            }
        }

        /// <summary>
        /// Event fired when the selected row changes in the CardGrid.  The event causes a new card to be previews in the left hand card preview area
        /// </summary>
        /// <param name="sender">CardGrid row that is selected</param>
        /// <param name="e"></param>
        private void CardDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //Load Image
            My.DataGrid grid = sender as My.DataGrid;

            UpdateSideViewCard(grid.SelectedItem as Card);
        }

        private void UpdateSideViewCard(Card card)
        {
            if (card != null)
            {
                PreviewImage.Source = ImageManager.GenerateImageSource(card.ImagePath);
                UpdatePreviewText(card);
            }
        }

        /// <summary>
        /// Handles sortin for the cardDataGrid (ToBeImplemented)
        /// </summary>
        /// <param name="sender">CardGrid column cell that is selected for sorting</param>
        /// <param name="e"></param>
       void CardDataGrid_Sorting(object sender, Microsoft.Windows.Controls.DataGridSortingEventArgs e)
        {

        }

        /// <summary>
        /// When the CardChi checkbox is checked, add the FilterChi card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardChi_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterChi);
        }

        /// <summary>
        /// When the CardChi checkbox is unchecked, remove the FilterChi card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardChi_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterChi);
        }

        /// <summary>
        /// When the CardCost checkbox is checked, add the FilterCost card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardCost_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterCost);
        }

        /// <summary>
        /// When the CardCost checkbox is unchecked, remove the FilterCost card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardCost_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterCost);
        }

        /// <summary>
        /// When the CardFocus checkbox is checked, add the FilterFocus card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardFocus_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterFocus);
        }

        /// <summary>
        /// When the CardFocus checkbox is unchecked, remove the FilterFocus card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardFocus_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterFocus);
        }

        /// <summary>
        /// When the CardForce checkbox is checked, add the FilterForce card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardForce_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterForce);
        }

        /// <summary>
        /// When the CardForce checkbox is unchecked, remove the FilterForce card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardForce_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterForce);
        }

        /// <summary>
        /// When the CardHonorReq checkbox is checked, add the FilterHonorReq card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardHonorReq_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterHonorReq);
        }

        /// <summary>
        /// When the CardHonorReq checkbox is unchecked, remove the FilterHonorReq card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardHonorReq_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterHonorReq);
        }

        /// <summary>
        /// When the CardKeyword checkbox is checked, add the FilterKeyword card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardKeyword_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterKeyword);
        }

        /// <summary>
        /// When the CardKeyword checkbox is unchecked, remove the FilterKeyword card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardKeyword_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterKeyword);
        }

        /// <summary>
        /// When the CardName checkbox is checked, add the FilterName card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardName_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterName);
        }

        /// <summary>
        /// When the CardName checkbox is unchecked, remove the FilterName card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardName_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterName);
        }

        /// <summary>
        /// When the CardPersonalHonor checkbox is checked, add the FilterPersonalHonor card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardPersonalHonor_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterPersonalHonor);
        }

        /// <summary>
        /// When the CardPersonalHonor checkbox is unchecked, remove the FilterPersonalHonor card filter
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ChkCardPersonalHonor_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterPersonalHonor);
        }

        /// <summary>
        /// When the CardText checkbox is checked, add the FilterText card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardText_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterText);
        }

        /// <summary>
        /// When the CardText checkbox is unchecked, remove the FilterText card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardText_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterText);
        }

        /// <summary>
        /// When the CardType checkbox is checked, add the FilterType card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardType_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterType);
        }

        /// <summary>
        /// When the CardType checkbox is unchecked, remove the FilterType card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkCardType_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterType);
        }

        /// <summary>
        /// When the CardClan checkbox is checked, add the FilterClan card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkClan_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterClan);
        }

        /// <summary>
        /// When the CardClan checkbox is unchecked, remove the FilterClan card filter
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ChkClan_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterClan);
        }

        /// <summary>
        /// When the CardLegality checkbox is checked, add the FilterLegality card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkLegality_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterLegality);
        }

        /// <summary>
        /// When the CardLegality checkbox is unchecked, remove the FilterLegality card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkLegality_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterLegality);
        }

        private void ChkRarity_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterRarity);
        }

        private void ChkRarity_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterRarity);
        }


        /// <summary>
        /// When the Set checkbox is checked, add the FilterSet card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkSet_Checked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter += new FilterEventHandler(FilterSet);
        }

        /// <summary>
        /// When the Set checkbox is unchecked, remove the FilterSet card filter
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void ChkSet_Unchecked(object sender, RoutedEventArgs e)
        {
            FilteredCardList.Filter -= new FilterEventHandler(FilterSet);
        }

        /// <summary>
        /// Removes all the CardControls from the Canvas Area
        /// </summary>
        private void ClearCanvas()
        {
            //Loop through the Canvas removing CardControls until they have all been removed. 
            List<CardControl> controls = DeckAreaCanvas.Children.OfType<CardControl>().ToList();
            foreach (CardControl control in controls)
            {
                DeckAreaCanvas.Children.Remove(control);
            }
        }

        /// <summary>
        /// Removes all the Cards from the in memory Deck.
        /// </summary>
        private void ClearDeck()
        {
            //Create a new GameDeck
            MyDeck = new GameDeck();
            //Reattach Bound Sources

            CollectionViewSource cvsDynasty = new CollectionViewSource();
            cvsDynasty.Source = MyDeck.DynastyDeck.Cards;
            DynastyGrid.DataContext = cvsDynasty.View;

            CollectionViewSource cvsFate = new CollectionViewSource();
            cvsFate.Source = MyDeck.FateDeck.Cards;
            FateGrid.DataContext = cvsFate;

            CollectionViewSource cvsStart = new CollectionViewSource();
            cvsStart.Source = MyDeck.StartDeck.Cards;
            StartGrid.DataContext = cvsStart;

            //Update the TextOverview
            UpdateOverviewText();

            //Update the Deck Text
            TxtDeck.Text = MyDeck.DeckAsText();
        }

        private void SideViewCard(object sender, RoutedEventArgs e)
        {
            CardControl control = sender as CardControl;
            UpdateSideViewCard(control.Card);
        }


        /// <summary>
        /// Detects when a Dragged object enters into the Canvas control
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments</param>
        private void DeckAreaCanvas_DragEnter(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.Move;
        }

        /// <summary>
        /// Detects when an object is Dragged over the Canvas control
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments</param>
        private void DeckAreaCanvas_DragOver(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.Move;
        }

        /// <summary>
        /// Detects when an object is Dropped into the Canvas Control
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments</param>
        private void DeckAreaCanvas_Drop(object sender, DragEventArgs e)
        {
            //Get the information from the Drag event arguments
            e.Effects = DragDropEffects.None;
            Point pCanvas = e.GetPosition(DeckAreaCanvas);
            Card draggedCard = e.Data.GetData("Card") as Card;

            //If the object being dragged is a Card and not null, then Add it to the Canvas and in memory Deck
            if (draggedCard != null)
            {
                e.Effects = DragDropEffects.Move;
                if (String.IsNullOrEmpty(AddCardToDeck(draggedCard)))
                    AddCardToCanvas(draggedCard, pCanvas);
            }
        }

        /// <summary>
        /// Detects when the mouse leaves the canvas area.  If a Pan was in process, the pan is stopped
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments, containing the mouse position</param>
        private void DeckAreaCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            DeckAreaCanvas.isPanInProgress = false;
        }

        /// <summary>
        /// Detects when the mouse is moved over the canvas area, and pans the canvas if a pan is in progress
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments, containing the mouse position</param>
        private void DeckAreaCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(e);

            //Perform a Pan of the Canvas
            if (DeckAreaCanvas.isPanInProgress)
            {
                //Get the originalPoint based on the user's original left click
                Point originalPoint = new Point(DeckAreaCanvas.origCursorLocation.X, DeckAreaCanvas.origCursorLocation.Y);
                //Get the current mouse position relative to the DeckCanvasArea
                Point currentPoint = e.GetPosition(DeckAreaCanvas);

                //Calculate the new CanvasCenter X and Y values
                double newX = CanvasCenter.X - (currentPoint.X - originalPoint.X) * .1;
                double newY = CanvasCenter.Y - (currentPoint.Y - originalPoint.Y) * .1;

                //Verify X values are still within the canvas parameters.
                if (newX < 0)
                    newX = 0;
                if (newX > DeckAreaCanvas.ActualWidth)
                    newX = DeckAreaCanvas.ActualWidth;

                //Verify Y values are still within the canvas parameters.
                if (newY < 0)
                    newY = 0;
                if (newY > DeckAreaCanvas.ActualHeight)
                    newY = DeckAreaCanvas.ActualHeight;

                //Create a new Center Point from the calculated panned values
                CanvasCenter = new Point(newX, newY);
                CanvasZoom.CenterX = newX;
                CanvasZoom.CenterY = newY;
            }
        }

        /// <summary>
        /// Detects when the mouse wheel is scrolled, and zooms in or out of the canvas
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Drag event arguments, containing the mouse wheel movement</param>
        private void DeckAreaCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            //Initialize scaling factors
            double scaleFactor = e.Delta * .001;
            double newScale = CanvasZoom.ScaleX + scaleFactor;

            //Check the lower bound of the scale factor
            if (newScale < 1)
            {
                CanvasZoom.ScaleX = 1;
                CanvasZoom.ScaleY = 1;
                ZoomSlider.Value = 1;
                return;
            }

            //Check the upper bound of the scale factor
            if (newScale > 7)
            {
                CanvasZoom.ScaleX = 7;
                CanvasZoom.ScaleY = 7;
                ZoomSlider.Value = 7;
                return;
            }

            //Set CanvasZoom scale factor
            CanvasZoom.ScaleX = newScale;
            CanvasZoom.ScaleY = newScale;

            //Set ZoomSlider value
            ZoomSlider.Value = newScale;
        }

        /// <summary>
        /// Exits the Deckeditor application, and closes all DeckEditor windows.
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e"></param>
        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Filters in memory card database via Chi by the TxtChiLow and TxtChiHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterChi(object sender, FilterEventArgs e)
        {
            //Get the card in question
            Card c = e.Item as Card;

            //Get the Chi LowerBound
            int lowChi = 0;
            if (TxtChiLow.Text.Length > 0 && IsInteger(TxtChiLow.Text))
            {
                lowChi = Convert.ToInt32(TxtChiLow.Text);
            }

            //Get the Chi HigherBound
            int highChi = 1000;
            if (TxtChiHigh.Text.Length > 0 && IsInteger(TxtChiHigh.Text))
            {
                highChi = Convert.ToInt32(TxtChiHigh.Text);
            }

            //Evaluate if the CardType has Chi, and if it is inside the specified Chi range
            int chi = 0;
            switch (c.CardType.ToString())
            {
                case "Follower":
                    chi = CardManager.ConvertCardValueToInt(((Follower)c).Chi);
                    if ((chi < lowChi) || (chi > highChi))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Item":
                    chi = CardManager.ConvertCardValueToInt(((Item)c).Chi);
                    if ((chi < lowChi) || (chi > highChi))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    chi = CardManager.ConvertCardValueToInt(((Personality)c).Chi);
                    if ((chi < lowChi) || (chi > highChi))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have Chi, so return false
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filters in memory card database via Clan by the CmbxClan value
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterClan(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;

            //Evaluate if the CardType has a clan, and if it matches the specified Clan value in the CmbxClan
            switch (c.CardType.ToString())
            {
                case "Stronghold":
                    if (((Stronghold)c).Clan != null)
                    {
                        if (((Stronghold)c).Clan.ToLower() != ((ComboBoxItem)CmbxClan.SelectedItem).Content.ToString().ToLower())
                        {
                            e.Accepted = false;
                        }
                    }
                    else
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    if (((Personality)c).Clan != null)
                    {
                        if (((Personality)c).Clan.ToLower() != ((ComboBoxItem)CmbxClan.SelectedItem).Content.ToString().ToLower())
                        {
                            e.Accepted = false;
                        }
                    }
                    else
                    {
                        e.Accepted = false;
                    }
                    break;

                case "Sensei":
                    Sensei card = (Sensei)c;
                    if (card != null)
                    {
                        if (!card.Keywords.Any(k => k.ToLower().Contains(CmbxClan.SelectedValue.ToString().ToLower())))
                        {
                            e.Accepted = false;
                        }
                    }
                    break;
                default:
                    //Card does not have a Clan value
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Apply Filter when index changes, and the FilterCardList has been loaded
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterCmbBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (FilteredCardList != null)
            {
                FilteredCardList.View.Refresh();
            }
        }

        /// <summary>
        /// Filters in memory card database via Cost by the TxtCostLow and TxtCostHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterCost(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;

            //Get the Gold Cost LowerBound
            int lowCost = 0;
            if (TxtCostLow.Text.Length > 0 && IsInteger(TxtCostLow.Text))
            {
                lowCost = Convert.ToInt32(TxtCostLow.Text);
            }

            //Get the GoldCost UpperBound
            int highCost = 1000;
            if (TxtCostHigh.Text.Length > 0 && IsInteger(TxtCostHigh.Text))
            {
                highCost = Convert.ToInt32(TxtCostHigh.Text);
            }

            //Evaluate if the CardType has a GoldCost, and if it is inside the specified Cost range
            int cost = 0;
            switch (c.CardType.ToString())
            {
                case "Strategy":
                    cost = CardManager.ConvertCardValueToInt(((EggOfPanKu.Library.Objects.Strategy)c).Focus);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Follower":
                    cost = CardManager.ConvertCardValueToInt(((Follower)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Holding":
                    cost = CardManager.ConvertCardValueToInt(((Holding)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Item":
                    cost = CardManager.ConvertCardValueToInt(((Item)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    cost = CardManager.ConvertCardValueToInt(((Personality)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Ring":
                    cost = CardManager.ConvertCardValueToInt(((Ring)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Spell":
                    cost = CardManager.ConvertCardValueToInt(((Spell)c).Cost);
                    if ((cost < lowCost) || (cost > highCost))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have a Gold Cost
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filters in memory card database via Focus by the TxtFocusLow and TxtFocusHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterFocus(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;

            //Get the Focus LowerBound
            int lowFocus = 0;
            if (TxtFocusLow.Text.Length > 0 && IsInteger(TxtFocusLow.Text))
            {
                lowFocus = Convert.ToInt32(TxtFocusLow.Text);
            }

            //Get the Gold Cost UpperBound
            int highFocus = 1000;
            if (TxtFocusHigh.Text.Length > 0 && IsInteger(TxtFocusHigh.Text))
            {
                highFocus = Convert.ToInt32(TxtFocusHigh.Text);
            }

            //Evaluate if the CardType has a Focus, and if it is inside the specified Focus range
            int focus = 0;
            switch (c.CardType.ToString())
            {
                case "Strategy":
                    focus = CardManager.ConvertCardValueToInt(((EggOfPanKu.Library.Objects.Strategy)c).Focus);
                    if ((focus < lowFocus) || (focus > highFocus))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Follower":
                    focus = CardManager.ConvertCardValueToInt(((Follower)c).Focus);
                    if ((focus < lowFocus) || (focus > highFocus))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Item":
                    focus = CardManager.ConvertCardValueToInt(((Item)c).Focus);
                    if ((focus < lowFocus) || (focus > highFocus))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Ring":
                    focus = CardManager.ConvertCardValueToInt(((Ring)c).Focus);
                    if ((focus < lowFocus) || (focus > highFocus))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Spell":
                    focus = CardManager.ConvertCardValueToInt(((Spell)c).Focus);
                    if ((focus < lowFocus) || (focus > highFocus))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have a Focus
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filters in memory card database via Force by the TxtForceLow and TxtForceHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterForce(object sender, FilterEventArgs e)
        {
            //Get the card in question
            Card c = e.Item as Card;

            //Get the Force LowerBound
            int lowForce = 0;
            if (TxtForceLow.Text.Length > 0 && IsInteger(TxtForceLow.Text))
            {
                lowForce = Convert.ToInt32(TxtForceLow.Text);
            }

            //Get the Force UpperBound
            int highForce = 1000;
            if (TxtForceHigh.Text.Length > 0 && IsInteger(TxtForceHigh.Text))
            {
                highForce = Convert.ToInt32(TxtForceHigh.Text);
            }

            //Evaluate if the CardType has a Force, and if it is inside the specified Force range
            int force = 0;
            switch (c.CardType.ToString())
            {
                case "Follower":
                    force = CardManager.ConvertCardValueToInt(((Follower)c).Force);
                    if ((force < lowForce) || (force > highForce))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Item":
                    force = CardManager.ConvertCardValueToInt(((Item)c).Force);
                    if ((force < lowForce) || (force > highForce))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    force = CardManager.ConvertCardValueToInt(((Personality)c).Force);
                    if ((force < lowForce) || (force > highForce))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have Force
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filters in memory card database via HonorRequirement by the TxtHonorReqLow and TxtHonorReqHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterHonorReq(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;

            //Get the HonorRequirement LowerBounds
            int lowHR = 0;
            if (TxtHonorReqLow.Text.Length > 0 && IsInteger(TxtHonorReqLow.Text))
            {
                lowHR = Convert.ToInt32(TxtHonorReqLow.Text);
            }

            //Get the HonorRequirement UpperBounds
            int highHR = 1000;
            if (TxtHonorReqHigh.Text.Length > 0 && IsInteger(TxtHonorReqHigh.Text))
            {
                highHR = Convert.ToInt32(TxtHonorReqHigh.Text);
            }

            //Evaluate if the CardType has an HonorRequirement, and if it is inside the specified range
            int hr = 0;
            switch (c.CardType.ToString())
            {
                case "Follower":
                    hr = CardManager.ConvertCardValueToInt(((Follower)c).HonorRequirement);
                    if ((hr < lowHR) || (hr > highHR))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    hr = CardManager.ConvertCardValueToInt(((Personality)c).HonorRequirement);
                    if ((hr < lowHR) || (hr > highHR))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have an Honor Requirement
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filters in memory card database using a card's list of keywords
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterKeyword(object sender, FilterEventArgs e)
        {
            //Get the card in question
            Card c = e.Item as Card;
            if (c == null || 
                !c.Keywords.Any(k => k.ToLower().Contains((string)TxtCardKeyword.Text.ToLower())))
            {
                e.Accepted = false;
            }
        }

        /// <summary>
        /// Filter in memory card database using a card's legality
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterLegality(object sender, FilterEventArgs e)
        {
            //Get the card in question
            Card c = e.Item as Card;
            if (c == null || !c.Legal.Contains((string)CmbxLegality.SelectedValue, StringComparer.OrdinalIgnoreCase))
            {
                e.Accepted = false;
            }
        }

        private void FilterRarity(object sender, FilterEventArgs e)
        {
            Card c = e.Item as Card;
            if (c == null || !c.Rarity.Equals((string)CmbxRarity.SelectedValue))
            {
                e.Accepted = false;
            }
        }

        /// <summary>
        /// Filters in memory card database via Card Name
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterName(object sender, FilterEventArgs e)
        {
            //Get the card in question
            Card c = e.Item as Card;

            //See if the card name contains the expression specified
            if (c == null || (!c.Name.ToLower().Contains(TxtCardName.Text.ToLower())))
            {
                e.Accepted = false;
            }
        }

        /// <summary>
        /// Filters in memory card database via PersonalHonor by the TxtPersonalHonorLow and TxtPersonalHonorHigh values
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterPersonalHonor(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;

            //Get the PersonalHonor LowerBounds
            int lowPH = 0;
            if (TxtPersonalHonorLow.Text.Length > 0 && IsInteger(TxtPersonalHonorLow.Text))
            {
                lowPH = Convert.ToInt32(TxtPersonalHonorLow.Text);
            }

            //Get the PersonalHonor UpperBounds
            int highPH = 1000;
            if (TxtPersonalHonorHigh.Text.Length > 0 && IsInteger(TxtPersonalHonorHigh.Text))
            {
                highPH = Convert.ToInt32(TxtPersonalHonorHigh.Text);
            }

            //Evaluate if the CardType has PersonalHonor, and if it is inside the specified range
            int ph = 0;
            switch (c.CardType.ToString())
            {
                case "Follower":
                    ph = CardManager.ConvertCardValueToInt(((Follower)c).PersonalHonor);
                    if ((ph < lowPH) || (ph > highPH))
                    {
                        e.Accepted = false;
                    }
                    break;
                case "Personality":
                    ph = CardManager.ConvertCardValueToInt(((Personality)c).PersonalHonor);
                    if ((ph < lowPH) || (ph > highPH))
                    {
                        e.Accepted = false;
                    }
                    break;
                default:
                    //Card does not have PersonalHonor
                    e.Accepted = false;
                    break;
            }
        }

        /// <summary>
        /// Filter Card View based on card Edition
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterSet(object sender, FilterEventArgs e)
        {
            Card c = e.Item as Card;
            
            if (c == null || (!c.Edition.Contains((string)CmbxSet.SelectedValue, StringComparer.OrdinalIgnoreCase)))
            {
                e.Accepted = false;
            }
        }

        /// <summary>
        /// Filters in memory card database via Card Text
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterText(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;
            //Check to see if the Card Text contains the expression specified
            if (c == null || (!c.Text.ToLower().Contains(TxtCardText.Text.ToLower())))
            {
                e.Accepted = false;
            }
        }
        /// <summary>
        /// Apply Filter when Enter key is pressed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FilterTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            //Check to see if key pressed is the Enter Key
            if (e.Key == Key.Enter)
            {
                StackPanel parent = sender as StackPanel;
                if (parent != null)
                {
                    foreach (UIElement child in parent.Children)
                    {
                        if (child.GetType() == typeof(CheckBox))
                        {
                            ((CheckBox)child).IsChecked = true;
                        }
                    }
                }
                FilteredCardList.View.Refresh();
            }
        }

        /// <summary>
        /// Update the filtered count list after the collection has been
        /// modified by a filter.
        /// </summary>
        private void FilteredCardListView_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            UpdateFilterCount(CardDataGrid.Items.Count, ((ObservableCollection<Card>)FilteredCardList.Source).Count);
        }

        /// <summary>
        /// Updates the filtered list label
        /// <para name="shown">Number of shown cards</para>
        /// <para name="total">Number of total cards</para>
        /// </summary>
        private void UpdateFilterCount(int shown, int total)
        {
            LblFilteredCount.Content = String.Format("Showing {0} of {1} Cards", shown, total);
        }

        /// <summary>
        /// Filters in memory card database via Card Type
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Filter event arguments, containing the card being evaluated</param>
        private void FilterType(object sender, FilterEventArgs e)
        {
            //Get the Card in question
            Card c = e.Item as Card;
            //Check to see if the CardType matches the selected Card Type
            if (c == null || c.CardType.ToString().ToLower() != ((ComboBoxItem)CmbxCardType.SelectedItem).Content.ToString().ToLower())
            {
                e.Accepted = false;
            }
        }

        private List<ComboBoxItem> PopulateClanItems(ObservableCollection<Card> c)
        {
            List<ComboBoxItem> itemList = new List<ComboBoxItem>();
            if (c != null && c.Count > 0)
            {
                var clans = (from cards in c
                             where cards.CardType == CardType.Stronghold
                             orderby ((Stronghold)cards).Clan
                             select ((Stronghold)cards).Clan).Distinct();

                foreach (string clan in clans)
                {
                    if (!String.IsNullOrEmpty(clan))
                    {
                        itemList.Add(new ComboBoxItem()
                        {
                            Tag = clan,
                            Content = clan
                        });
                    }
                }
            }
            return itemList;
        }

        private List<ComboBoxItem> PopulateCardTypes(ObservableCollection<Card> c)
        {
            List<ComboBoxItem> itemList = new List<ComboBoxItem>();
            if (c != null && c.Count > 0)
            {
                var types = (from cards in c
                             orderby cards.CardTypeName
                             select cards.CardTypeName).Distinct();

                foreach (string type in types)
                {
                    if (!String.IsNullOrEmpty(type))
                    {
                        itemList.Add(new ComboBoxItem()
                        {
                            Tag = type,
                            Content = type
                        });
                    }
                }
            }
            return itemList;
        }

        private List<ComboBoxItem> PopulateSetItems(ObservableCollection<Card> c)
        {
            List<ComboBoxItem> itemList = new List<ComboBoxItem>();
            if (c != null && c.Count > 0)
            {
                var editions = c.SelectMany(cards => cards.Edition).Distinct();
                foreach (string edition in editions)
                {
                    if (!String.IsNullOrEmpty(edition))
                    {
                        itemList.Add(new ComboBoxItem()
                        {
                            Tag = edition,
                            Content = edition
                        });
                    }
                }
            }
            return itemList;
        }

        private List<ComboBoxItem> PopulateLegalityItems(ObservableCollection<Card> c)
        {
            List<ComboBoxItem> itemList = new List<ComboBoxItem>();
            if (c != null && c.Count > 0)
            {
                var legalities = c.SelectMany(cards => cards.Legal).Distinct();
                foreach (string legality in legalities)
                {
                    if(!String.IsNullOrEmpty(legality)) 
                    {
                        itemList.Add(new ComboBoxItem()
                        {
                            Tag = legality,
                            Content = legality
                        });
                    }
                }
            }
            return itemList;
        }

        private List<ComboBoxItem> PopulateRarityItems(ObservableCollection<Card> c)
        {
            List<ComboBoxItem> itemList = new List<ComboBoxItem>();
            if (c != null && c.Count > 0)
            {
                foreach (string rarity in c.Select(cards => cards.Rarity).Distinct())
                {
                    if (!String.IsNullOrEmpty(rarity))
                    {
                        itemList.Add(new ComboBoxItem()
                        {
                            Tag = rarity,
                            Content = rarity
                        });
                    }
                }
            }
            return itemList;
        }

        /// <summary>
        /// Load the Card XML File into Memory
        /// </summary>
        /// <param name="cardXmlFilePath">Path to the Card XML File</param>
        private void LoadCardDB(string cardXmlFilePath)
        {
            try
            {
                //Create and Load the Card Database
                CardDB = CardDatabase.LoadCardDatabase(cardXmlFilePath);

                FilteredCardList.Source = new ObservableCollection<Card>(CardDatabase.GetAllCards(CardDB));
                ObservableCollection<Card> c = FilteredCardList.Source as ObservableCollection<Card>;

                if (CardDB != null && CardDB.Count > 0)
                {
                    FilteredCardList.View.CollectionChanged +=
                        new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FilteredCardListView_CollectionChanged);
                }

                //Populate filter drop downs with information from files
                CmbxClan.ItemsSource = PopulateClanItems(c);
                if (CmbxClan.Items.Count > 0)
                    CmbxClan.SelectedIndex = 0;

                CmbxCardType.ItemsSource = PopulateCardTypes(c);
                if (CmbxCardType.Items.Count > 0)
                    CmbxCardType.SelectedIndex = 0;

                CmbxSet.ItemsSource = PopulateSetItems(c);
                if (CmbxSet.Items.Count > 0)
                    CmbxSet.SelectedIndex = 0;

                CmbxLegality.ItemsSource = PopulateLegalityItems(c);
                if (CmbxLegality.Items.Count > 0)
                    CmbxLegality.SelectedIndex = 0;

                CmbxRarity.ItemsSource = PopulateRarityItems(c);
                if (CmbxRarity.Items.Count > 0)
                    CmbxRarity.SelectedIndex = 0;

                //Grid for all Loaded Cards from XML file(s)
                CardDataGrid.DataContext = FilteredCardList.View;

                //Label showing visible/loaded cards
                UpdateFilterCount(c.Count, c.Count);
            }
            catch (System.Exception exc)
            {
                App.Log.Error(exc);
                throw;
            }
        }

        /// <summary>
        /// Called when the LoadCardsWindow is Submitted.  Will use the newly specified Card.XML file to load the in memory Card database
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Routed Event arguments</param>
        private void LoadCardsWindow_SubmitCardFile(object sender, RoutedEventArgs e)
        {
            //Load Card.XML from UserXmlPath Setting
            string cardXmlFile = ConfigManager.AppSettingsMgr.CardXmlPath;
            LoadCardDB(cardXmlFile);
        }

        /// <summary>
        /// Menu Item that opens a File Dialog box for the user to choose a new Card.XML file.
        /// </summary>
        /// <param name="sender">Sender object of the event</param>
        /// <param name="e">Routed Event arguments</param>
        private void LoadCardXml_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog();
            openFile.DefaultExt = ".xml";
            openFile.Filter = "Card File (.xml)|*.xml";

            bool? result = openFile.ShowDialog();
            if (result == true)
            {
                ConfigManager.AppSettingsMgr.CardXmlPath = openFile.FileName;
                ConfigManager.AppSettingsMgr.Save();
                InitializeControls();
            }
        }

        /// <summary>
        /// Menu Item event that lets the user choose a folder for where the CardImages are located.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LoadImages_Click(object sender, RoutedEventArgs e)
        {
            //Use old Windows Forms for the Folder Browser Dialog box
            System.Windows.Forms.FolderBrowserDialog folderDialog = new System.Windows.Forms.FolderBrowserDialog();
            folderDialog.ShowDialog();

            //Verify Folder exists
            if (Directory.Exists(folderDialog.SelectedPath))
            {
                //Save it to the config file and re-Initialize the controls
                ConfigManager.AppSettingsMgr.CardImagePath = folderDialog.SelectedPath;
                ConfigManager.AppSettingsMgr.Save();
                InitializeControls();
            }
        }

        /// <summary>
        /// Menu Item to create a new deck.  This will clear the canvas and in memory deck of all cards.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NewDeck_Click(object sender, RoutedEventArgs e)
        {
            ClearCanvas();
            ClearDeck();
        }

        /// <summary>
        /// Menu Item to open a deck specified by an OpenFileDialog, and load it into the interface
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenDeck_Click(object sender, RoutedEventArgs e)
        {
            //Open OpenFileDialog
            Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog();
            openFile.DefaultExt = ".l5dx";
            openFile.Filter = "L5R XML Decks(.l5dx)|*.l5dx|L5R Decks (.l5d)|*.l5d";

            //Load Selected .L5D file
            bool? result = openFile.ShowDialog();
            if (result == true)
            {
                //If we did not cancel the dialog, update the Deck Editor
                OpenDeckFile(openFile.FileName);
            }

        }

        private void OpenDeckFile(string fileName)
        {
            //Remove all Cards
            ClearDeck();
            ClearCanvas();

            FileInfo fi = new FileInfo(fileName);
            MyDeck.DeckName = fi.Name.Replace(fi.Extension, "");
            MyDeck.FileName = fileName;
            MyDeck.LoadDeck(fileName);

            DynastyGrid.DataContext = MyDeck.DynastyDeck.Cards;
            FateGrid.DataContext = MyDeck.FateDeck.Cards;
            StartGrid.DataContext = MyDeck.StartDeck.Cards;

            //Populate Text tab of deck list
            TxtDeck.Text = MyDeck.DeckAsText();

            //Update text at bottom that shows deck totals
            UpdateOverviewText();
            LayoutTabs.SelectedItem = LayoutTabDeck;

            //Organize Cards in the available space
            OrganizeCanvas();
        }

        /// <summary>
        /// Organizes cards in the Canvas control into a designated layout
        /// </summary>
        private void OrganizeCanvas()
        {
            int xOffset = 0;
            int yOffset = 0;

            //Add StartsInPlay Deck to Canvas
            foreach (Card card in MyDeck.StartDeck.Cards)
            {
                for (int i = 0; i < card.CountInDeck; i++)
                {
                    AddCardToCanvas(card, new Point(5, 5 + (i * 10) + (yOffset * 100)));
                }
                yOffset++;
            }

            //Add Dynasty Deck to Canvas
            xOffset = 0;
            yOffset = 0;
            foreach (Card dynastyCard in MyDeck.DynastyDeck.Cards)
            {
                for (int i = 0; i < dynastyCard.CountInDeck; i++)
                {
                    if (130 + (xOffset * 50) > DeckAreaCanvas.ActualWidth)
                    {
                        xOffset = 0;
                        yOffset++;
                    }
                    AddCardToCanvas(dynastyCard, new Point(65 + (xOffset * 50), 10 * i + 50 + (yOffset * 150)));
                }
                xOffset++;
            }

            //Add Fate Deck to Canvas
            foreach (Card fateCard in MyDeck.FateDeck.Cards)
            {
                for (int i = 0; i < fateCard.CountInDeck; i++)
                {
                    if (130 + (xOffset * 50) > DeckAreaCanvas.ActualWidth)
                    {
                        xOffset = 0;
                        yOffset++;
                    }
                    AddCardToCanvas(fateCard, new Point(65 + (xOffset * 50), 10 * i + 50 + (yOffset * 150)));
                }
                xOffset++;
            }
        }

        /// <summary>
        /// Open the specified card in a Preview window
        /// </summary>
        /// <param name="card">Card to open in the Preview window</param>
        private void PreviewCard(Card card)
        {
            PreviewWindow pw = new PreviewWindow();
            pw.LoadCard(card, ImagePath);
            pw.Show();
        }

        /// <summary>
        /// Remove the specified CardControl from the Canvas
        /// </summary>
        /// <param name="cardControl">CardControl to remove</param>
        private void RemoveCardFromCanvas(CardControl cardControl)
        {
            if (cardControl != null)
            {
                //Remove all the events tied to the CardControl
                cardControl.AddCard -= new CardControl.AddCardHandler(AddCard_Click);
                cardControl.RemoveCard -= new CardControl.RemoveCardHandler(RemoveCard_Click);
                cardControl.ViewCard -= new CardControl.ViewCardHandler(ViewCard_Click);

                //Remove the CardControl from the canvas
                DeckAreaCanvas.Children.Remove(cardControl);
            }
        }

        /// <summary>
        /// Remove the specified card from the in memory Deck
        /// </summary>
        /// <param name="card">Card to remove</param>
        private void RemoveCardFromDeck(Card card)
        {
            MyDeck.RemoveCard(card, 1);
            UpdateOverviewText();
            TxtDeck.Text = MyDeck.DeckAsText();
        }

        /// <summary>
        /// Right Click Menu event that removes the specified Card from the Canvas and in memory Deck
        /// </summary>
        /// <param name="sender">CardControl that was right clicked</param>
        /// <param name="e">Routed Event Arguments</param>
        private void RemoveCard_Click(object sender, RoutedEventArgs e)
        {
            CardControl cardControl = sender as CardControl;
            RemoveCardFromCanvas(cardControl);
            RemoveCardFromDeck(cardControl.Card);
        }

        /// <summary>
        /// Menu item event that performs a Save As on the current deck
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveDeckAs_Click(object sender, RoutedEventArgs e)
        {
            //If a location was chosen for the save, then Save the Deck.
            string file = SaveDeckDialog();
            if (!String.IsNullOrEmpty(file))
            {
                FileInfo fi = new FileInfo(file);
                MyDeck.DeckName = fi.Name.Replace(fi.Extension, "");
                MyDeck.FileName = file;
                if(String.IsNullOrEmpty(MyDeck.SaveDeck()))
                    MessageBox.Show(String.Format("Deck saved as {0}", MyDeck.FileName));
            }
        }

        private string SaveDeckDialog()
        {
            //Open a Save File Dialog window
            Microsoft.Win32.SaveFileDialog saveFile = new Microsoft.Win32.SaveFileDialog();
            saveFile.DefaultExt = ".l5dx";
            saveFile.Filter = "L5R Decks (.l5dx)|*.l5dx";

            //If a location was chosen for the save, then Save the Deck.
            bool? result = saveFile.ShowDialog();
            if (result == true)
            {
                return saveFile.FileName;
            }
            else 
            {
                return String.Empty;
            }
        }

        /// <summary>
        /// Menu item even that performs a Save on the current deck
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Routed Event Arguments</param>
        private void SaveDeck_Click(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrEmpty(MyDeck.FileName))
                MyDeck.FileName = SaveDeckDialog();
            if(!String.IsNullOrEmpty(MyDeck.FileName)) 
            {
                if(String.IsNullOrEmpty(MyDeck.SaveDeck()))
                    MessageBox.Show(String.Format("Deck saved as {0}", MyDeck.FileName));
            }
        }

        /// <summary>
        /// Update the Deck overview text at the bottom of window
        /// </summary>
        private void UpdateOverviewText()
        {
            //If there is a Stronghold Card specified in the deck, display its name in the Deck Overview section
            if (MyDeck.StrongholdCard != null)
            {
                TxtStronghold.Text = MyDeck.StrongholdCard.Name;
            }
            else
            {
                TxtStronghold.Text = "";
            }

            //Display the number of Dynasty and Fate cards in the deck
            TxtDynastyCards.Text = MyDeck.DynastyDeck.Cards.Sum(cards => cards.CountInDeck).ToString() + " cards";
            TxtFateCards.Text = MyDeck.FateDeck.Cards.Sum(cards => cards.CountInDeck).ToString() + " cards";
        }

        /// <summary>
        /// Update the Preview Area to display the specified Card image and information
        /// </summary>
        /// <param name="card">Card to preview in the preview section of the window</param>
        private void UpdatePreviewText(Card card)
        {
            PreviewTxtName.Text = card.Name;
            PreviewTxtCardType.Text = card.CardType.ToString();
            PreviewTxtRarity.Text = card.Rarity;
            switch (card.CardType)
            {
                case CardType.Ancestor:
                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Ancestor)card).Focus;
                    PreviewStackForceChi.Visibility = Visibility.Visible;
                    PreviewTxtForce.Text = ((Ancestor)card).Force;
                    PreviewTxtChi.Text = ((Ancestor)card).Chi;
                    PreviewStackHonorGold.Visibility = Visibility.Visible;
                    PreviewTxtGC.Text = ((Ancestor)card).Cost;
                    PreviewTxtHR.Text = ((Ancestor)card).HonorRequirement;
                    PreviewTxtPH.Text = ((Ancestor)card).PersonalHonor;
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Celestial:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Event:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Follower:
                    PreviewStackSH.Visibility = Visibility.Collapsed;

                    PreviewStackForceChi.Visibility = Visibility.Visible;
                    PreviewTxtForce.Text = ((Follower)card).Force;
                    PreviewTxtChi.Text = ((Follower)card).Chi;

                    PreviewStackHonorGold.Visibility = Visibility.Visible;
                    PreviewTxtHR.Text = ((Follower)card).HonorRequirement;
                    PreviewTxtGC.Text = ((Follower)card).Cost;
                    PreviewTxtPH.Text = ((Follower)card).PersonalHonor;

                    PreviewStackGold.Visibility = Visibility.Collapsed;

                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Follower)card).Focus;
                    break;
                case CardType.Holding:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;

                    PreviewStackGold.Visibility = Visibility.Visible;
                    PreviewTxtGold.Text = ((Holding)card).Cost;

                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Item:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Visible;
                    PreviewTxtForce.Text = ((Item)card).Force;
                    PreviewTxtChi.Text = ((Item)card).Chi;

                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;

                    PreviewStackGold.Visibility = Visibility.Visible;
                    PreviewTxtGold.Text = ((Item)card).Cost;

                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Item)card).Focus;
                    break;
                case CardType.Personality:
                    PreviewStackSH.Visibility = Visibility.Collapsed;

                    PreviewStackForceChi.Visibility = Visibility.Visible;
                    PreviewTxtForce.Text = ((Personality)card).Force;
                    PreviewTxtChi.Text = ((Personality)card).Chi;

                    PreviewStackHonorGold.Visibility = Visibility.Visible;
                    PreviewTxtHR.Text = ((Personality)card).HonorRequirement;
                    PreviewTxtGC.Text = ((Personality)card).Cost;
                    PreviewTxtPH.Text = ((Personality)card).PersonalHonor;

                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Region:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Ring:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;

                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Ring)card).Focus;
                    break;
                case CardType.Sensei:
                    PreviewStackSH.Visibility = Visibility.Visible;
                    PreviewTxtPS.Text = ((Sensei)card).Strength.ToString();
                    PreviewTxtGP.Text = ((Sensei)card).Gold.ToString();
                    PreviewTxtStartHonor.Text = ((Sensei)card).StartingHonor.ToString();

                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Sensei)card).Focus;
                    break;

                case CardType.Spell:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;

                    PreviewStackGold.Visibility = Visibility.Visible;
                    PreviewTxtGold.Text = ((Spell)card).Cost;

                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Spell)card).Focus;
                    break;
                case CardType.Strategy:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;

                    PreviewStackGold.Visibility = Visibility.Visible;
                    PreviewTxtGold.Text = ((Strategy)card).Cost;

                    PreviewStackFocus.Visibility = Visibility.Visible;
                    PreviewTxtFocus.Text = ((Strategy)card).Focus;
                    break;
                case CardType.Stronghold:
                    PreviewStackSH.Visibility = Visibility.Visible;
                    PreviewTxtPS.Text = ((Stronghold)card).Strength.ToString();
                    PreviewTxtGP.Text = ((Stronghold)card).Gold.ToString();
                    PreviewTxtStartHonor.Text = ((Stronghold)card).StartingHonor.ToString();

                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
                case CardType.Wind:
                    PreviewStackSH.Visibility = Visibility.Collapsed;
                    PreviewStackForceChi.Visibility = Visibility.Collapsed;
                    PreviewStackHonorGold.Visibility = Visibility.Collapsed;
                    PreviewStackGold.Visibility = Visibility.Collapsed;
                    PreviewStackFocus.Visibility = Visibility.Collapsed;
                    break;
            }

            PreviewWBText.NavigateToString("<div style=\"text-align:center;font-size:12\">" + card.Text.Replace("<br>", "<br/><br/>") + "</div>");

            StringBuilder legalEditions = new StringBuilder("");
            foreach (string edition in card.Edition.ToArray())
            {
                legalEditions.Append(edition + ",");
            }
            if (legalEditions.Length > 0)
            {
                legalEditions.Length--;
            }
            PreviewTxtLegality.Text = legalEditions.ToString();

            PreviewTxtCardId.Text = card.CardID;
        }

        /// <summary>
        /// RightClick menu event that will trigger the opening of a Preview window for the selected Card
        /// </summary>
        /// <param name="sender">CardControl that triggered the RightClick event</param>
        /// <param name="e">Routed Event Arguments</param>
        private void ViewCard_Click(object sender, RoutedEventArgs e)
        {
            //Get the CardControl, and send it to the Preview window
            CardControl cardControl = sender as CardControl;
            PreviewCard(cardControl.Card);
        }

        /// <summary>
        /// Event that detects when the ZoomSlider control's value changes, and updates the Canvas zoom appropriately
        /// </summary>
        /// <param name="sender">Sender of the event (Zoom Slider)</param>
        /// <param name="e">Routed Property Changed Event Arguments</param>
        private void ZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            CanvasZoom.ScaleX = ZoomSlider.Value;
            CanvasZoom.ScaleY = ZoomSlider.Value;
        }

        #endregion Methods
    }
}